package com.jdd.modules.parking.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jdd.common.api.vo.Result;
import com.jdd.common.client.ParkCloudHttpClient;
import com.jdd.common.client.vo.SynObjectVO;
import com.jdd.common.constant.BooleanTypeKind;
import com.jdd.common.constant.JddConstant;
import com.jdd.common.exception.JddException;
import com.jdd.common.system.query.QueryGenerator;
import com.jdd.common.util.AsynRestTmplateUtils;
import com.jdd.common.util.SnowFlakeUtil;
import com.jdd.modules.parkcar.entity.ParkCarMonthly;
import com.jdd.modules.parkcar.entity.ParkSpace;
import com.jdd.modules.parkcar.service.IParkCarMonthlyService;
import com.jdd.modules.parkcar.service.IParkSpaceService;
import com.jdd.modules.parkcar.service.IParkWhiteListService;
import com.jdd.modules.parking.entity.*;
import com.jdd.modules.parking.mapper.ConfigParkInfoMapper;
import com.jdd.modules.parking.mapper.ParkCarEnterLogMapper;
import com.jdd.modules.parking.mapper.ParkCarOutLogMapper;
import com.jdd.modules.parking.mapper.ZombieCarMapper;
import com.jdd.modules.parking.service.IParkArmInfoService;
import com.jdd.modules.parking.service.IZombieCarService;
import com.jdd.modules.parking.service.IparkCarEnterLogService;
import com.jdd.modules.parkmonthly.entity.MonthlyCarConfig;
import com.jdd.modules.parkmonthly.service.IMonthlyCarConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author jdd
 */
@Slf4j
@Service
public class ZombieCarImpl extends ServiceImpl<ZombieCarMapper, ZombieCar> implements IZombieCarService {
    @Resource
    private ConfigParkInfoMapper configParkInfoMapper;
    @Resource
    private IParkWhiteListService parkWhiteListService;
    @Resource
    private ParkCarEnterLogMapper parkCarEnterLogMapper;
    @Resource
    private IparkCarEnterLogService parkCarEnterLogService;
    @Resource
    private ParkCarOutLogMapper parkCarOutLogMapper;
    @Resource
    private IParkArmInfoService parkArmInfoService;
    @Resource
    private IParkCarMonthlyService parkCarMonthlyService;
    @Resource
    private IParkSpaceService parkSpaceService;
    @Resource
    private ParkCloudHttpClient parkCloudHttpClient;

    @Override
    public Result<IPage<ZombieCar>> findZombieList(ZombieCar zombieCar, Integer pageNo, Integer pageSize, HttpServletRequest req) {

        Result<IPage<ZombieCar>> result = new Result<>();
        QueryWrapper<ZombieCar> queryWrapper = Wrappers.query();
        if (StringUtils.isNotBlank(zombieCar.getPlateNo())) {
            StringBuilder plateNoMatchStr = new StringBuilder("");
            for (int i = 0; i < zombieCar.getPlateNo().length(); i++) {
                plateNoMatchStr.append(zombieCar.getPlateNo().charAt(i));
                if (i != 0 && i != zombieCar.getPlateNo().length() - 1) {
                    plateNoMatchStr.append('%');
                }
            }
            queryWrapper.lambda().like(ZombieCar::getPlateNo, plateNoMatchStr);
        }
        if(StringUtils.isNotBlank(zombieCar.getCarNature())){
            queryWrapper.lambda().eq(ZombieCar::getCarNature,zombieCar.getCarNature());
        }
        //得到拼接条件
        addZombieCarQueryWrapper(queryWrapper);
        //分页查找
        Page<ZombieCar> page = new Page<>(pageNo, pageSize);
        IPage<ZombieCar> pageList = this.page(page, queryWrapper);
        List<ZombieCar> records = pageList.getRecords();
        //给集合带上滞留天数
        records.forEach(item -> {
            long stayDay = DateUtil.between(DateUtil.parse(item.getCarEnterTime(), DatePattern.NORM_DATETIME_FORMAT), new Date(), DateUnit.DAY);
            item.setStayDay(stayDay);
        });
        result.setSuccess(true);
        result.setResult(pageList);
        return result;
    }

    /**
     * 拼装滞留车查询条件
     */
    public void addZombieCarQueryWrapper(QueryWrapper<ZombieCar> queryWrapper) {
        ConfigParkInfo info = configParkInfoMapper.queryConfigParkInfo();
        if (ObjectUtil.isEmpty(info)) {
            throw new JddException(500, "车场配置为空！！！");
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DAY_OF_MONTH, -info.getVenueStayDays());
        queryWrapper.lambda().eq(ZombieCar::getIsOut, "0");
        queryWrapper.lambda().in(ZombieCar::getIsTransfer, "0","1");
        //要查询大于配置滞留天数
        queryWrapper.lambda().le(ZombieCar::getCarEnterTime, calendar.getTime());
        queryWrapper.lambda().orderByDesc(ZombieCar::getCarEnterTime);
    }

    //查询剔除黑名单的滞留车数量
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer getZombieNumer() {
        //查询车场信息僵尸车时间的定义
        ConfigParkInfo info = configParkInfoMapper.queryConfigParkInfo();
        if (ObjectUtil.isEmpty(info)) {
            return 0;
        }
        QueryWrapper<ZombieCar> queryWrapper = new QueryWrapper<>();
        addZombieCarQueryWrapper(queryWrapper);
        return this.list(queryWrapper).size();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void zombieToOut(String plateNo, String armId) {
        ParkArmInfo parkArmInfo = parkArmInfoService.getById(armId);
        if (null == parkArmInfo) {
            log.error("车道信息不存在");
            throw new JddException(500, "车道信息不存在");
        }
        LambdaQueryWrapper<ParkCarEnterLog> enterLogWrapper = Wrappers.lambdaQuery();
        enterLogWrapper.eq(ParkCarEnterLog::getPlateNo, plateNo);
        enterLogWrapper.eq(ParkCarEnterLog::getIsOut, "0");
        enterLogWrapper.eq(ParkCarEnterLog::getParkCode, parkArmInfo.getParkCode());
        enterLogWrapper.orderByDesc(ParkCarEnterLog::getCarEnterTime);
        List<ParkCarEnterLog> carEnterLogs = parkCarEnterLogMapper.selectList(enterLogWrapper);
        if (ObjectUtil.isEmpty(carEnterLogs)) {
            log.error("---------------滞留车未找到入场纪录--------------");
            throw new JddException(500, "未查询到记录");
        }
        ParkCarEnterLog lastEnterLog = carEnterLogs.get(0);
        List<SynObjectVO> synObjectVOList = new ArrayList<>();
        if (StringUtils.isNotBlank(lastEnterLog.getParkNumber())) {
            this.monthlyCarToOut(lastEnterLog,synObjectVOList);
        }
        // 最近的一条入场记录是临时车记录
        this.normalCarCreateOutLog(lastEnterLog, parkArmInfo,synObjectVOList);
        for(ParkCarEnterLog enterLog:carEnterLogs)
        {
            ParkCarEnterLog updateEnterLog = new ParkCarEnterLog();
            updateEnterLog.setId(enterLog.getId());
            updateEnterLog.setIsOut("1");
            synObjectVOList.add(SynObjectVO.getUpdateObject(updateEnterLog));
        }
        this.parkCarEnterLogMapper.setOutByBatch(carEnterLogs.stream().map(ParkCarEnterLog::getId).collect(Collectors.toList()));
        parkCloudHttpClient.synSetParkEnterLogOut(synObjectVOList);
    }

    @Override
    //滞留车转黑名单
    public boolean zombieToBlack(String id) {
        ParkCarEnterLog carEnterLog = parkCarEnterLogMapper.selectById(id);
        if (ObjectUtil.isEmpty(carEnterLog)) {
            log.error("---------------滞留车未找到入场纪录--------------");
            throw new JddException(500, "未查询到记录");
        }
        return parkWhiteListService.zombieToBlack(carEnterLog);
    }

    private void normalCarCreateOutLog(ParkCarEnterLog carEnterLog, ParkArmInfo parkArmInfo,List<SynObjectVO> synObjectVOList) {
        //生成个出场纪录
        ParkCarOutLog parkCarOutLog = new ParkCarOutLog();
        BeanUtils.copyProperties(carEnterLog, parkCarOutLog);
        parkCarOutLog.setId(SnowFlakeUtil.getId());
        //选择出口的车道
        if (ObjectUtil.isEmpty(parkArmInfo)) {
            //滞留车转出场没有选择车道
            parkCarOutLog.setArmName(null);
            parkCarOutLog.setSerialNo(null);
            parkCarOutLog.setIpAddr(null);
        } else {
            //滞留车转出场有选择车道
            parkCarOutLog.setCarEnterId(carEnterLog.getParkNumber());
            parkCarOutLog.setArmName(parkArmInfo.getArmName());
            parkCarOutLog.setSerialNo(parkArmInfo.getSerialNo());
            parkCarOutLog.setIpAddr(parkArmInfo.getIpAddr());
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        parkCarOutLog.setCreateTime(new Date());
        parkCarOutLog.setCarOutTime(sdf.format(new Date()));
        parkCarOutLog.setRemark("滞留车辆手动出场");
        //加上工作站名和岗亭人账户
        parkCarOutLog.setWorkName("管理员工作站");
        parkCarOutLog.setSysUserName("管理员");
        SynObjectVO synObjectVO = SynObjectVO.getAddObject(parkCarOutLog);
        synObjectVOList.add(synObjectVO);
        parkCarOutLogMapper.insert(parkCarOutLog);
        log.info("---------------滞留车入场记录转出场成功--------------");
    }

    private void monthlyCarToOut(ParkCarEnterLog parkCarEnterLog,List<SynObjectVO> synObjectVOList) {
        // 获取在该区域下，月卡绑定的所有月租车信息
        List<ParkCarMonthly> parkCarMonthlyList = parkCarMonthlyService.getAllMonthlyInfoByAreaIdAndMonthlyCardId(parkCarEnterLog.getMonthlyCardId(),parkCarEnterLog.getAreaId());
        if(parkCarMonthlyList.isEmpty())
        {
            log.info("当前车辆的月租信息已注销，无需转卡");
            return;
        }
        // 获取在该区域下，月卡绑定的车位信息
        List<ParkSpace> spaces = parkSpaceService.getParkCarNumber(parkCarEnterLog.getMonthlyCardId(),parkCarEnterLog.getAreaId());
        if(spaces.isEmpty())
        {
            log.info("当前车辆绑定的月卡信息异常-》该区域下未绑定车位");
            return;
        }
        List<ParkSpace> transSpaces = spaces.stream().filter(space->space.getParkNumber().equals(parkCarEnterLog.getParkNumber())).collect(Collectors.toList());
        if(transSpaces.isEmpty())
        {
            log.info("当前车辆占用的车位信息异常-》不存在该占用车位，车位号：{}",parkCarEnterLog.getParkNumber());
            return;
        }
        ParkSpace transSpace = transSpaces.get(0);
        // 获取该区域下，已获得车位的车牌号
        List<String> inSitePlateNos = spaces.stream().map(ParkSpace::getPlateNo).distinct().collect(Collectors.toList());
        // 获取在该区域下，月卡绑定的月租车车牌号
        List<String> allPlateNos = parkCarMonthlyList.stream().map(ParkCarMonthly::getPlateNo).distinct().collect(Collectors.toList());
        if(spaces.size()> inSitePlateNos.size())
        {
            // 车位数辆大于已获得车位的车牌号数量，说明存在空闲车位
            log.info("当前车场存在空闲车位无需转卡");
            parkSpaceService.updateSpaceNoPlate(transSpace);
            SynObjectVO synObjectVO = SynObjectVO.getUpdateObject(transSpace);
            synObjectVOList.add(synObjectVO);
            return;
        }
        if(allPlateNos.size()==inSitePlateNos.size())
        {
            // 月卡在该区域下所有的月租车都已获得车位
            log.info("当前月卡绑定的月租车辆皆获得车位，无需转卡");
            parkSpaceService.updateSpaceNoPlate(transSpace);
            SynObjectVO synObjectVO = SynObjectVO.getUpdateObject(transSpace);
            synObjectVOList.add(synObjectVO);
            return;
        }
        // 得到未获得车位的车牌号
        allPlateNos.removeAll(inSitePlateNos);
        // 获得这些车牌号的在该区域的停放纪录
        LambdaQueryWrapper<ParkCarEnterLog> monthlyEnterLogWrapper = Wrappers.lambdaQuery();
        monthlyEnterLogWrapper.in(ParkCarEnterLog::getPlateNo,allPlateNos);
        monthlyEnterLogWrapper.eq(ParkCarEnterLog::getIsOut,"0");
        monthlyEnterLogWrapper.eq(ParkCarEnterLog::getThisArea,"1");
        monthlyEnterLogWrapper.eq(ParkCarEnterLog::getAreaId,parkCarEnterLog.getAreaId());
        monthlyEnterLogWrapper.orderByAsc(ParkCarEnterLog::getCarEnterTime);
        List<ParkCarEnterLog> inSiteCarEnterLogs = parkCarEnterLogMapper.selectList(monthlyEnterLogWrapper);
        if(CollUtil.isEmpty(inSiteCarEnterLogs))
        {
            // 停放纪录为空
            log.info("未获得车位的月租车无该区域的入场纪录，无需转卡");
            parkSpaceService.updateSpaceNoPlate(transSpace);
            SynObjectVO synObjectVO = SynObjectVO.getUpdateObject(transSpace);
            synObjectVOList.add(synObjectVO);
            return;
        }
        String transPlateNo = inSiteCarEnterLogs.get(0).getPlateNo();
        List<ParkCarEnterLog> transLogs = inSiteCarEnterLogs.stream().filter(log->log.getPlateNo().equals(transPlateNo)).collect(Collectors.toList());
        ParkCarEnterLog transLog = transLogs.get(transLogs.size()-1);
        log.info("获得需要转卡的纪录：{}",transLog);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.SECOND,1);
        transLog.setCarEnterTime(DateUtil.format(calendar.getTime(),DatePattern.NORM_DATETIME_FORMAT));
        transLog.setId(SnowFlakeUtil.getId());
        transLog.setIsTransfer(JddConstant.ParkCarEnterLog.IS_TRANSFER_ALREADY);
        transLog.setParkNumber(parkCarEnterLog.getParkNumber());
        transLog.setCarTypeId(parkCarEnterLog.getCarTypeId());
        transLog.setMonthlyCardId(parkCarEnterLog.getMonthlyCardId());
        transLog.setCarNature(parkCarEnterLog.getCarNature());
        parkCarEnterLogMapper.insert(transLog);
        SynObjectVO synObjectVO = SynObjectVO.getAddObject(transLog);
        synObjectVOList.add(synObjectVO);
        log.info("转卡纪录保存成功：{}",transLog);
        parkSpaceService.updateSpaceCar(transSpace,transLog.getPlateNo());
        synObjectVO = SynObjectVO.getUpdateObject(transSpace);
        synObjectVOList.add(synObjectVO);
    }
}
